using System;
using System.Collections.Generic;
using System.Text;

namespace ImagesMath
{
public class VR3
{

    #region private
    private double x, y, z;

    #endregion

    #region Constructors

    public VR3(double x, double y, double z) 
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }
        public VR3(PR3 p) 
        {
            this.x = p.X;
            this.y = p.Y;
            this.z = p.Z;
        }
        public VR3(PR3 p1, PR3 p2) 
        { 
            this.x = p2.X - p1.X;
            this.y = p2.Y - p1.Y;
            this.z = p2.Z - p1.Z;
        }

      #endregion

    #region Accessors

    public double X
    {
        get { return this.x; }
        set {this.x = value; }
    }
    public double Y
    {
        get { return this.y; }
        set { this.y = value; }
    }
    public double Z
    {
        get { return this.z; }
        set { this.z = value; }
    }

    #endregion

    #region Arithmetical operators

    public static VR3 operator +(VR3 v1, VR3 v2)
    {
        return new VR3(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
    }


    public static VR3 operator -(VR3 v1, VR3 v2)
    {
        return new VR3(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
    }

    public static VR3 operator -(VR3 v1)
    {
        return new VR3(-v1.X, -v1.Y, -v1.Z);
    }

    public static VR3 operator *(VR3 v1, double k)
    {
        return new VR3(v1.X * k, v1.Y * k, v1.Z * k);
    }

    public static VR3 operator / (VR3 v1, double k)
    {
        if (k  >  ImageMath.EPS)
        {
            return new VR3(v1.X / k, v1.Y / k, v1.Z / k);
        }
        else
        {
            return v1;
        }
    }

    public static double operator !(VR3 v1) // square of the vector magnitude
    {
        return v1.X * v1.X + v1.Y * v1.Y + v1.Z * v1.Z;
    }

    public static double operator %(VR3 v1, VR3 v2)
    {
        return (v1.X * v2.X) + (v1.Y * v2.Y) + (v1.Z * v2.Z);
    }

    public static VR3 operator ^(VR3 v1, VR3 v2)
    {
        return new VR3(v1.Y * v2.Z - v1.Z * v2.Y, v1.Z * v2.X - v1.X * v2.Z, v1.X * v2.Y - v1.Y * v2.X);
    }


    public static bool  operator == (VR3 v1,VR3 v2)
    {
        return (v1.X == v2.X && v1.Y == v2.Y && v1.Z == v2.Z);

    }
    public static bool operator !=(VR3 v1, VR3 v2)
    {
        try
        {
            return (!(v1.X == v2.X && v1.Y == v2.Y && v1.Z == v2.Z));
        }
        catch (Exception e)
        {
            return true;
        }
    }

    #endregion

    #region functions

    //// friendly functions
    public VR3 normalize() 
    {
        VR3 v = this;
        double n = !v;
        double a = Math.Sqrt(n);
        
        if (n > 0)
        {
            v = v / a;
        }
        
        return v;
    }

    public VR3 normalize(VR3 v1)
    {
        double n = !v1;
        if (n > ImageMath.EPS)
        {
            return v1 / Math.Sqrt(n);
        }
        return null;
    }

   

    public double dot(VR3 v1, VR3 v2)
    {
        return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z;
    }


    public VR3 cross(VR3 v1, VR3 v2)
    {
        return new VR3(v1.Y * v2.Z - v1.Z * v2.Y, v1.Z * v2.X - v1.X * v2.Z, v1.X * v2.Y - v1.Y * v2.X);
    }
    public VR3 unitCross(VR3 v1, VR3 v2)
    {
        return this.normalize(this.cross(v1, v2));
    }
    public double mixedProduct(VR3 v1, VR3 v2, VR3 v3)
    {
        return dot(cross(v1, v2), v3);
    }

    public void orthonormalize(VR3 u, VR3 v, VR3 w)
    {
        // If the input vectors are v0, v1, and v2, then the Gram-Schmidt
        // orthonormalization produces vectors u0, u1, and u2 as follows,
        //
        //   u0 = v0/|v0|
        //   u1 = (v1-(u0*v1)u0)/|v1-(u0*v1)u0|
        //   u2 = (v2-(u0*v2)u0-(u1*v2)u1)/|v2-(u0*v2)u0-(u1*v2)u1|
        //
        // where |A| indicates length of vector A and A*B indicates dot
        // product of vectors A and B.
        // Gram-Schmidt orthonormalization take linearly independent vectors
        // U, V, and W and compute an orthonormal set (unit length, mutually
        // perpendicular).

        // compute u0
          u = u.normalize();

        // compute u1
          double d0 = (u%v);
          v -= u*d0;
          v = v.normalize();

        // compute u2
          double d1 = (v%w);
          d0 = u%w;
          w -= u*d0 + v*d1;
          w = w.normalize();
    }



    #endregion
    
 }

}
